package run.halo.app.controller.content;

import static org.springframework.data.domain.Sort.Direction.DESC;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.SortDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.util.HtmlUtils;
import run.halo.app.model.entity.Post;
import run.halo.app.model.vo.PostListVO;
import run.halo.app.service.OptionService;
import run.halo.app.service.PostService;
import run.halo.app.service.ThemeService;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

/**
 * Search controller.
 *
 * @author ryanwang
 * @date 2019-04-21
 */
@Controller
@RequestMapping(value = "/search")
public class ContentSearchController {

    private final PostService postService;

    private final OptionService optionService;

    private final ThemeService themeService;

    public ContentSearchController(PostService postService, OptionService optionService,
        ThemeService themeService) {
        this.postService = postService;
        this.optionService = optionService;
        this.themeService = themeService;
    }

    /**
     * Render post search page.
     *
     * @param model model
     * @param keyword keyword
     * @return template path : themes/{theme}/search.ftl
     */
    @GetMapping
    public String search(Model model,
        @RequestParam(value = "keyword") String keyword) {
        return this.search(model, HtmlUtils.htmlEscape(keyword), 1, Sort.by(DESC, "createTime"));
    }

    /**
     * Render post search page.
     *
     * @param model model
     * @param keyword keyword
     * @return template path :themes/{theme}/search.ftl
     */
    @GetMapping(value = "page/{page}")
    public String search(Model model,
        @RequestParam(value = "keyword") String keyword,
        @PathVariable(value = "page") Integer page,
        @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
        final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), sort);
        // 搜索的时候，将搜索的英文进行 原字符串、小写字符串、大写字符串 三种类型的搜索，保证搜到所有结果
        final Page<Post> postPageRaw = postService.pageBy(keyword, pageable);
        final Page<Post> postPageLower = postService.pageBy(keyword.toLowerCase(), pageable);
        final Page<Post> postPageUpper = postService.pageBy(keyword.toUpperCase(), pageable);

        // 通过查看数据类型，发现搜索结果都保存在Page<Post>类型对象的的content元素中
        // 因此遍历所有content中的搜索结果, 保存到pageContentList中
        List<Post> pageContentList = new ArrayList<>();

        // 遍历原始字符串搜索结果
        if(postPageRaw.getContent().size() != 0){
            for (int i =0; i< postPageRaw.getContent().size() ; i++){
                pageContentList.add(postPageRaw.getContent().get(i));
            }
        }
        // 遍历Lower字符串搜索结果
        if(postPageLower.getContent().size() != 0){
            for (int i =0; i< postPageLower.getContent().size() ; i++){
                pageContentList.add(postPageLower.getContent().get(i));
            }
        }
        // 遍历Upper字符串搜索结果
        if(postPageUpper.getContent().size() != 0){
            for (int i =0; i< postPageUpper.getContent().size() ; i++){
                pageContentList.add(postPageUpper.getContent().get(i));
            }
        }

        // 将上述搜索结果保存到set里面，去除重复，再转换成List
        LinkedHashSet<Post> hashContentSet = new LinkedHashSet<>(pageContentList);
        List<Post> hashContentList = new ArrayList<>(hashContentSet);

        // 这里参考 https://stackoverflow.com/questions/37136679/how-to-convert-a-list-of-enity-object-to-page-object-in-spring-mvc-jpa
        Page<Post> page_new = new PageImpl<>(hashContentList);
        final Page<PostListVO> posts = postService.convertToListVo(page_new);

        // 这是原始方法, 不对keyword进行任何处理直接搜索
        // final Page<Post> postPage = postService.pageBy(keyword, pageable);
        // final Page<PostListVO> posts = postService.convertToListVo(postPage);

        model.addAttribute("is_search", true);
        model.addAttribute("keyword", HtmlUtils.htmlEscape(keyword));
        model.addAttribute("posts", posts);
        model.addAttribute("meta_keywords", optionService.getSeoKeywords());
        model.addAttribute("meta_description", optionService.getSeoDescription());
        return themeService.render("search");
    }
}
